use fs_protocol_lib::*;
use std::collections::HashMap;
use prost_types::Any;
use tonic::{transport::Server, Request, Response as TonicResponse, Status};

#[derive(Default)]
struct FsProtocolLibServer {
    users: HashMap<String, String>,
}

mod fs_protocol_lib;

#[tonic::async_trait]
impl fs_protocol_lib_server::FsProtocolLib for FsProtocolLibServer {
    async fn login(
        &self,
        request: Request<LoginRequest>,
    ) -> Result<TonicResponse<Response>, Status> {
        // create a new Any type with value of String.
        let my_string = "Hello, world!".to_owned();
        let mut any = Any {
            type_url: "type.googleapis.com/google.protobuf.StringValue".to_owned(),
            value: my_string.into_bytes(),
        };

// unpack the Any type to String.
        let string_value = any.unpack::<String>().unwrap();

        let username = request.get_ref().username.to_owned();
        let password = request.get_ref().password.to_owned();
        if let Some(actual_password) = self.users.get(&username) {
            return if actual_password == &password {
                let response = Response {
                    msg: "Login successful".to_string(),
                    code: Code::Ok as i32,
                    data: Option::from(Any { type_url: "".to_string(), value: vec![] }),
                };
                let mut a = Any::new();
                Any::pack();
                Ok(TonicResponse::new(response))
            } else {
                let response = Response {
                    msg: "Invalid password".to_string(),
                    code: Code::InvalidPassword as i32,
                    data: Default::default(),
                };
                Ok(TonicResponse::new(response))
            };
        }

        let response = Response {
            msg: "Invalid username".to_string(),
            code: Code::InvalidUsername as i32,
            data: Default::default(),
        };
        Ok(TonicResponse::new(response))
    }

    async fn upgrade(
        &self,
        request: Request<UpgradeRequest>,
    ) -> Result<TonicResponse<Response>, Status> {
        let download_url = request.get_ref().download.to_owned();
        let package_name = request.get_ref().name.to_owned();

        let response = Response {
            msg: format!("Downloading {} from {}", package_name, download_url),
            code: Code::Ok as i32,
            data: Default::default(),
        };
        Ok(TonicResponse::new(response))
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr = "[::1]:50051".parse()?;
    let server = FsProtocolLibServer {
        users: {
            let mut map = HashMap::new();
            map.insert("alice".to_string(), "pass123".to_string());
            map.insert("bob".to_string(), "secret456".to_string());
            map
        },
    };
    Server::builder()
        .add_service(fs_protocol_lib_server::FsProtocolLibServer::new(server))
        .serve(addr)
        .await?;
    Ok(())
}